package com.moli.iov.handle.alarm.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.moli.iov.handle.alarm.biz.valid.alarm.AlarmingValidHandler;
import com.moli.iov.handle.alarm.biz.valid.disarmalarm.DisarmAlarmValidHandler;
import com.moli.iov.handle.alarm.biz.utils.AlarmUtils;
import com.moli.iov.handle.alarm.constants.Constant;
import com.moli.iov.handle.alarm.enums.AlarmCodeEnum;
import com.moli.iov.handle.alarm.enums.AlarmCodeStatusEnum;
import com.moli.iov.handle.alarm.enums.AlarmStatusEnum;
import com.moli.iov.handle.alarm.enums.MeAuditStatusEnum;
import com.moli.iov.handle.alarm.model.dto.AlarmDto;
import com.moli.iov.handle.alarm.model.dto.AlarmOnLineDto;
import com.moli.iov.handle.alarm.model.dto.DisarmAlarmDto;
import com.moli.iov.handle.alarm.model.dto.VehicleBaseDto;
import com.moli.iov.handle.alarm.model.po.AlarmInfo;
import com.moli.iov.handle.alarm.model.po.AlarmLocation;
import com.moli.iov.handle.alarm.model.po.AlarmMileageInfo;
import com.moli.iov.handle.alarm.model.po.AlarmVehicleInfo;
import com.moli.iov.handle.alarm.model.po.extend.AlarmMileageExceptionExtend;
import com.moli.iov.handle.alarm.model.po.extend.FenceInfoExtend;
import com.moli.iov.handle.alarm.mq.kafka.KafkaProducer;
import com.moli.iov.handle.alarm.notice.AlarmNotice;
import com.moli.iov.handle.alarm.service.IAlarmInfoRequestService;
import com.moli.iov.handle.alarm.service.IAlarmInfoService;
import com.moli.iov.handle.alarm.service.IAlarmOtherService;
import com.moli.iov.real.model.res.VehicleStatusDto;
import com.moli.iov.real.service.IRealService;
import com.moli.iov.rest.RestResponse;
import com.moli.iov.util.CommonUtils;
import com.moli.iov.util.DateUtil;
import com.moli.iov.util.SnowflakeUtils;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 *
 * </p>
 *
 * @author: zql
 * @Date: 2019/2/18 10:08
 */
@Slf4j
@Service("alarmInfoRequestService")
public class AlarmInfoRequestServiceImpl implements IAlarmInfoRequestService {

  @Autowired
  private IAlarmInfoService alarmInfoService;

  @Autowired
  private AlarmUtils alarmUtils;

  @Autowired
  private AlarmingValidHandler alarmingValidHandler;

  @Autowired
  private DisarmAlarmValidHandler disarmAlarmValidHandler;

  @Autowired
  private KafkaProducer kafkaProducer;

  @Autowired
  private IAlarmOtherService alarmOtherService;

  @Autowired
  private AlarmNotice alarmNotice;
  @Autowired
  private IRealService realService;

  /**
   * 报警
   */
  @Override
  public void alarm(AlarmDto alarmDto) {

    if (!alarmingValidHandler.doHandle(alarmDto)) {
      return;
    }

    if (alarmDto.getIsCheckAlarming() &&
        checkOfIsAlarming(alarmDto.getVin(), alarmDto.getAlarmCode())) {
      return;
    }

    if (updateOfIgnore(alarmDto)) {
      return;
    }

    AlarmInfo alarmInfo = assemblyAlarmInfo(alarmDto);

    alarmInfoService.save(alarmInfo);

    alarmDto.setLicense(alarmInfo.getLicense());
    alarmAfterBiz(alarmDto);
  }

  /**
   * 更新忽略为报警中
   */
  @Override
  public boolean updateOfIgnore(AlarmDto alarmDto) {
    AlarmInfo alarmInfoQuery = new AlarmInfo();
    alarmInfoQuery.setVin(alarmDto.getVin());
    alarmInfoQuery.setAlarmCode(alarmDto.getAlarmCode());
    alarmInfoQuery.setStatus(AlarmStatusEnum.IGNORE.getFlag());
    List<AlarmInfo> alarmInfos = alarmInfoService.listAlarmInfo(alarmInfoQuery);
    if (alarmInfos.isEmpty()) {
      return false;
    }

    AlarmInfo alarmInfo = alarmInfos.get(0);
    if (alarmInfo == null ||
        alarmInfo.getBeginTime() == null) {
      return false;
    }

    Long beginTime = alarmInfo.getBeginTime().getTime() / Constant.MILLISECONDS;
    if (beginTime < Constant.IGNORE_ALARM_TIME) {
      log.info("更新忽略为报警中 失败，当前处在忽略期，不允许添加新的报警");
      return true;
    }

    alarmInfo.setStatus(AlarmStatusEnum.ALARMING.getFlag());
    alarmInfoService.updateAlarmOfIgnore(alarmInfo);
    log.info("把忽略中的数据更新为报警中，不再添加新的报警 alarmDto:{}", alarmDto);
    return true;
  }

  /**
   * 报警后续业务
   */
  @Override
  public void alarmAfterBiz(AlarmDto alarmDto) {
    try{
      log.info("报警后续业务 发送kafka 报警消息 vin:{} 数据：{}", alarmDto.getVin(), alarmDto);
      kafkaProducer.send(alarmDto);
    }catch (Exception e){
      log.error("报警后续业务 发送kafka 报警消息 异常", e);
    }

    try{
      log.info("报警后续业务 更改缓存报警状态 vin:{} alarmCode:{} 报警类型：{} 报警状态：{}",
          alarmDto.getVin(),
          alarmDto.getAlarmCode(),
          AlarmCodeEnum.getMsg(alarmDto.getAlarmCode()),
          AlarmCodeStatusEnum.ALARMING.getMsg());

      alarmUtils.setAlarmCodeStatus(
          alarmDto.getVin(), alarmDto.getAlarmCode(), AlarmCodeStatusEnum.ALARMING.getFlag());
    }catch(Exception e){
      log.error("报警后续业务 更改缓存报警状态异常", e);

    }

    try{
      //报警通知
      alarmNotice.trigger(alarmDto.getVin(), alarmDto.getAlarmCode());
    }catch(Exception e){
      log.error("报警通知 异常", e);
    }
  }

  /**
   * 添加报警 组装数据
   */
  @Override
  public AlarmInfo assemblyAlarmInfo(AlarmDto alarmDto) {
    AlarmInfo alarmInfo = new AlarmInfo();
    try {
      CommonUtils.copyProperties(alarmDto, alarmInfo);

      alarmInfo.setAlarmName(AlarmCodeEnum.getMsg(alarmInfo.getAlarmCode()));
      alarmInfo.setAlarmId(SnowflakeUtils.getId());
      alarmInfo.setStatus(AlarmStatusEnum.ALARMING.getFlag());
      alarmInfo.setBeginTime(new Date());
      alarmInfo.setLocation(new AlarmLocation(alarmDto.getLongitude(), alarmDto.getLatitude()));
      alarmInfo.setAlarmMileageInfo(
          new AlarmMileageInfo(alarmDto.getCurrentMileage(), alarmDto.getThresholdMileage()));

      if (alarmDto.getAlarmCode() == AlarmCodeEnum.MILEAGE_EXCEPTION_CHANGE.getFlag()) {
        AlarmMileageExceptionExtend alarmMileageExceptionExtend = new AlarmMileageExceptionExtend(
            alarmDto.getLastTimeMileage(), alarmDto.getCurrentMileage(),
            alarmDto.getDifferenceMileage(), alarmDto.getMilExThreshold());
        alarmMileageExceptionExtend.setActualMileage(alarmDto.getActualMileage());
        alarmMileageExceptionExtend.setGpsMileage(alarmDto.getGpsMileage());
        alarmInfo.setMileageExceptionChange(alarmMileageExceptionExtend);
        alarmInfo.setMeAuditStatus(MeAuditStatusEnum.UNAUDITED.getFlag());
      }

      if(alarmDto.getAlarmCode() == AlarmCodeEnum.OUT_FENCE.getFlag() ||
          alarmDto.getAlarmCode() == AlarmCodeEnum.IN_FENCE.getFlag()){
        RestResponse<Map<String, VehicleStatusDto>> res = realService.getVehicleStatusByVin(Arrays.asList(alarmDto.getVin()));
       if (res!=null&&res.getData()!=null){
         VehicleStatusDto vehicleStatusDto = res.getData().get(alarmDto.getVin());
         if (vehicleStatusDto!=null){
           alarmInfo.setActualMileage(vehicleStatusDto.getOdo());
           alarmInfo.setGpsMileage(vehicleStatusDto.getGpsOdo());
         }
       }

        FenceInfoExtend fenceInfo = new FenceInfoExtend();
        fenceInfo.setFenceId(alarmDto.getFenceId());
        fenceInfo.setFenceName(alarmDto.getFenceName());
        alarmInfo.setFenceInfo(fenceInfo);
      }

      VehicleBaseDto vehicleBaseDto = alarmOtherService.queryVehicleBaseTbox(alarmInfo.getVin());
      AlarmVehicleInfo alarmVehicleInfo = new AlarmVehicleInfo();
      CommonUtils.copyProperties(vehicleBaseDto, alarmVehicleInfo);
      alarmInfo.setAlarmVehicleInfo(alarmVehicleInfo);
      alarmInfo.setLicense(vehicleBaseDto.getLicense());
      alarmInfo.setCompanyId(vehicleBaseDto.getCompanyId());
      alarmInfo.setCompanyName(vehicleBaseDto.getCompanyName());

      Date lastReportDate = alarmOtherService.getRealTimeInfo(alarmInfo.getVin());
      alarmInfo.setLastReportDate(lastReportDate);
      log.info("添加报警 lastReportDate:{} 组装数据:{}", lastReportDate, JSONObject.toJSONString(alarmInfo));
      return alarmInfo;
    } catch (Exception e) {
      log.error("报警 组装数据 异常 入参：{}", alarmInfo, e);
      return alarmInfo;
    }
  }

  /**
   * 取消报警
   */
  @Override
  public void disarmAlarm(DisarmAlarmDto disarmAlarmDto) {

    if (!disarmAlarmValidHandler.doHandle(disarmAlarmDto)) {
      return;
    }

    AlarmInfo alarmInfo = assemblyDisarmAlarmInfo(disarmAlarmDto);

    log.info("取消报警 vin:{} alarmCode:{} 报警类型：{}",
        alarmInfo.getVin(),
        alarmInfo.getAlarmCode(),
        AlarmCodeEnum.getMsg(alarmInfo.getAlarmCode()));
    this.updateAlarm(alarmInfo);

    disarmAlarmAfterBiz(disarmAlarmDto);
  }

  /**
   * 取消报警组装数据
   */
  @Override
  public AlarmInfo assemblyDisarmAlarmInfo(DisarmAlarmDto disarmAlarmDto) {

    AlarmInfo alarmInfo = new AlarmInfo();
    alarmInfo.setVin(disarmAlarmDto.getVin());
    alarmInfo.setAlarmCode(disarmAlarmDto.getAlarmCode());
    alarmInfo.setEndTime(new Date());
    alarmInfo.setRelieveUserId(Constant.DEFAULT_USER_ID);
    alarmInfo.setRelieveUserName(Constant.DEFAULT_USER_NAME);

    if (disarmAlarmDto.getLatitude() != null && disarmAlarmDto.getLongitude() != null) {
      AlarmLocation alarmLocation = new AlarmLocation();
      alarmLocation.setLongitude(disarmAlarmDto.getLongitude());
      alarmLocation.setLatitude(disarmAlarmDto.getLatitude());
      alarmInfo.setDisarmLocation(alarmLocation);
    }

    try {
      AlarmInfo alarmInfoOld = getAlarmInfo(
          alarmInfo.getVin(),
          alarmInfo.getAlarmCode());

      if (alarmInfoOld != null) {

        long duration = alarmInfo.getEndTime().getTime() - alarmInfoOld.getBeginTime().getTime();

        alarmInfo.setDuration(duration);
        alarmInfo.setDurationStr(DateUtil.formatTimeStr(duration));
      }
    } catch (Exception e) {
      log.error("取消报警组装数据 异常 入参:{}", disarmAlarmDto, e);
    }

    return alarmInfo;
  }

  /**
   * 取消报警 后续 业务 处理
   */
  @Override
  public void disarmAlarmAfterBiz(DisarmAlarmDto disarmAlarmDto) {

    try{
      log.info("取消报警 后续业务  缓存当前报警类型的状态  vin:{} alarmCode:{} 报警类型：{} 报警状态：{}",
          disarmAlarmDto.getVin(),
          disarmAlarmDto.getAlarmCode(),
          AlarmCodeEnum.getMsg(disarmAlarmDto.getAlarmCode()),
          AlarmCodeStatusEnum.ALARM_CANCELLED.getMsg());
      alarmUtils.setAlarmCodeStatus(disarmAlarmDto.getVin(),
          disarmAlarmDto.getAlarmCode(),
          AlarmCodeStatusEnum.ALARM_CANCELLED.getFlag());
    }catch (Exception e){
      log.error("取消报警 后续业务  缓存当前报警类型的状态异常", e);
    }

    try {
      alarmUtils.deleteIgnoreAlarm(disarmAlarmDto.getVin(), disarmAlarmDto.getAlarmCode());
    }catch (Exception e){
      log.error("取消报警 移除忽略报警信息", e);
    }

  }

  /**
   * 取消报警
   *
   * @param alarmInfo 车架号
   */
  @Override
  public void updateAlarm(AlarmInfo alarmInfo) {

    alarmInfoService.updateAlarm(alarmInfo);
  }

  /**
   * 添加离线集合
   */
  @Override
  public void addOffline(AlarmOnLineDto alarmOnLineDto) {
    alarmUtils.setOffLine(alarmOnLineDto);
  }

  /**
   * 判断当前报警编码 是否正在报警
   */
  @Override
  public boolean checkOfIsAlarming(String vin, int alarmCode) {

    AlarmInfo alarmInfo = getAlarmInfo(vin, alarmCode);

    return alarmInfo != null ? AlarmCodeStatusEnum.ALARMING.getFlag() :
        AlarmCodeStatusEnum.ALARM_CANCELLED.getFlag();
  }

  /**
   * 根据报警编码 查询 正在报警的数据
   */
  @Override
  public AlarmInfo getAlarmInfo(String vin, int alarmCode) {

    AlarmInfo alarmInfoQuery = new AlarmInfo();
    alarmInfoQuery.setVin(vin);
    alarmInfoQuery.setAlarmCode(alarmCode);
    alarmInfoQuery.setStatus(AlarmStatusEnum.ALARMING.getFlag());

    List<AlarmInfo> alarmInfos = alarmInfoService.listAlarmInfo(alarmInfoQuery);

    return alarmInfos == null || alarmInfos.isEmpty() ? null : alarmInfos.get(0);
  }
}

